package org.phpgwt.phpgenerator;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.phpgwt.Utility;
import org.phpgwt.annotations.File;
import org.phpgwt.annotations.Getters;
import org.phpgwt.annotations.JsonSerializable;
import org.phpgwt.annotations.PublicFields;
import org.phpgwt.annotations.Setters;

import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class PHPGenerator {
	public static String generatePHP(LinkerContext context) throws Exception {
		ArrayList<String> php = new ArrayList<String>();
		php.addAll(Utility.readFileFromClasspath("org/phpgwt/phpgenerator/header.inc"));
		php.add("");

		List<String> classes = Utility.getConfigurationProperties(context, "phpgwt_classes");

		if (classes != null && classes.size() > 0) {
			php.add("### Generated classes");
			php.add("");

			for (String clazz : classes) {
				Class<?> clazzClass = Class.forName(clazz);
				String className = Utility.getShortClassName(clazz);
				String jsonSerializable = clazzClass.isAnnotationPresent(JsonSerializable.class) ? "implements JsonSerializable" : "";
				String visibility = clazzClass.isAnnotationPresent(PublicFields.class) ? "public" : "private";

				php.add(String.format("class %s %s{", className, jsonSerializable));

				for (Field field : clazzClass.getDeclaredFields()) {
					php.add(String.format("  %s $%s;", visibility, field.getName()));
				}
				php.add("");
				
				if (clazzClass.isAnnotationPresent(Getters.class) || clazzClass.isAnnotationPresent(Setters.class)) {
					for (Field field : clazzClass.getDeclaredFields()) {
						String fieldName = Utility.firstLetterToUpperCase(field.getName());

						if (clazzClass.isAnnotationPresent(Getters.class)) {
							php.add(String.format("  public function get%s(){", fieldName));
							php.add(String.format("    return $this->%s;", field.getName()));
							php.add("  }");
							php.add("");
						}
						//
						if (clazzClass.isAnnotationPresent(Setters.class)) {
							php.add(String.format("  public function set%s($%s){", fieldName, field.getName()));
							php.add(String.format("    $this->%s = $%s;", field.getName(), field.getName()));
							php.add("  }");
						}
					}
				}

				php.add("}");
				php.add("");
			}
		}

		php.add("### Service definitions");
		php.add("");

		for (String className : Utility.getConfigurationProperties(context, "phpgwt_services")) {
			String asyncServiceFullyQualifiedName = className + "Async";
			final String shortClassName = Utility.getShortClassName(className);

			Class<?> ifaceClass = Class.forName(className);
			File file = ifaceClass.getAnnotation(File.class);

			php.add(String.format("$files['%s'] = '%s';", shortClassName, file != null ? file.value() : shortClassName + ".php"));
			php.add(String.format("$services['%s'] = array();", shortClassName));

			Class<?> clazz = Class.forName(asyncServiceFullyQualifiedName);

			for (int j = 0; j < clazz.getMethods().length; j++) {
				Method method = clazz.getMethods()[j];
				try {
					if (method.getParameterTypes().length == 0) {
						throw new Exception(String.format("No parameters found for method '%s'.", method.getName()));
					} else {
						Class<?> lastParamClazz = method.getParameterTypes()[method.getParameterTypes().length - 1];

						if (!AsyncCallback.class.equals(lastParamClazz)) {
							throw new Exception(String.format("The last parameter of method '%s' if not AsyncCallback.", method.getName()));
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
					throw new Exception(String.format("Invalid interface '%s'", asyncServiceFullyQualifiedName));
				}

				php.add(String.format("$services[\"%s\"][] = \"%s\";", shortClassName, method.getName()));
			}
			php.add("");
		}

		php.addAll(Utility.readFileFromClasspath("org/phpgwt/phpgenerator/footer.inc"));

		return Utility.arrayListToString(php);
	}
}
